/* ===================================
Michal Kowalczyk
2010 EZI
==================================== */

#include "stdafx.h"
#include "engine.h"

using namespace std;

STLogger * STLogger::_instance = 0;

STLogger & STLogger::Instance() 
{
	if(_instance == 0 )
		_instance = new STLogger();

	return *_instance;
}

STLogger::STLogger()
{
	m_filename = "log.txt";
	m_lvl = 0;
}

bool STLogger::setup(string filename, const int & lvl, string appname, string appver)
{
	if(!filename.empty())
		m_filename = filename;

	m_lvl = lvl;

	ofstream logfile;
	logfile.open(m_filename.c_str(), fstream::out);
	if(logfile.is_open()) {

		time_t rawtime;
		time ( &rawtime );

		logfile << "Simple logging system for: \"" << appname << "\"" << endl;
		logfile << "Application version: " << appver << endl;
		char buffer[32];
		ctime_s(buffer, sizeof(buffer), &rawtime);
		logfile << "This log file was created on: " << buffer << endl;
		logfile << endl;

		logfile.close();
	}
	else {
		m_lvl = 0;
		return false;
	}

	return true;
}

void STLogger::log(string msg, const int & isError) {
	if(!m_lvl)
		return;
	if( (m_lvl == 1) && (isError == 0) )
		return;

	ofstream logfile;
	logfile.open(m_filename.c_str(), fstream::out | fstream::app);
	if(logfile.is_open())
	{
		logfile << msg << endl;
		logfile.close();
	}
	return;
}

void STLogger::log(string msg, const int & value, const int & isError) {
	if(!m_lvl)
		return;
	if( (m_lvl == 1) && (isError == 0) )
		return;

	stringstream tmp;
	tmp << value;
	msg.append(tmp.str());

	ofstream logfile;
	logfile.open(m_filename.c_str(), fstream::out | fstream::app);
	if(logfile.is_open())
	{
		logfile << msg << endl;
		logfile.close();
	}
	return;
}

// =====================================================
// ENTITY MANAGER
// =====================================================

EntityManager * EntityManager::_instance = 0;

EntityManager & EntityManager::Instance()
{
	if(_instance == 0 )
		_instance = new EntityManager();

	return *_instance;
};

bool EntityManager::addEntity(EntityID id, EntityPtr entity) 
{
	size += 1;
	EntityMap::iterator it = m_entityMap.begin();
	m_entityMap.insert(it, std::pair< EntityID, EntityPtr >(id, entity) );
	return true;
}

// =====================================================
// PROCESS MANAGER
// =====================================================
ProcessManager * ProcessManager::_instance = 0;

ProcessManager & ProcessManager::Instance() 
{
	if(_instance == 0 )
		_instance = new ProcessManager();

	return *_instance;
}

bool ProcessManager::attach( ProcessPtr process )
{
	m_processList.push_back(process);
	//STLogger::Instance().log("process attached ", (long)&process, 0);
	return true;
}

void ProcessManager::updateProcesses()
{
	i = m_processList.begin();
	end = m_processList.end();

	ProcessPtr pNext;

	while ( i != end)
	{
		ProcessPtr p( *i );
		++i;

		if( p->isDead )
		{ 
			//STLogger::Instance().log("p->isDead ", (long)&p, 0);
			pNext = p->getNext();
			if(pNext)
			{
				//	STLogger::Instance().log("pNext ", (long)&p, 0);
				p->setNext(ProcessPtr((Process *)NULL));
				attach(pNext);
			}
			detach( p );
		}
		else if( p->m_active )
			p->update();
	}
}

bool ProcessManager::detach(ProcessPtr process)
{

	m_processList.remove(process);
	//STLogger::Instance().log("process detached ", (long)&process, 0);
	return true;
}


bool Core::running(true);
Core::Core(string appName, string appVersion): app_name(appName), app_ver(appVersion), logger(STLogger::Instance())
{	
	// initialize keys table
	for(int i = 0; i < 512; i++)
		keys[i] = 0;

	// TURN ON LOGGER
	logger.setup("log.txt", Core::getSettings().log_level, app_name, app_ver);	

	// LOAD SETTINGS FROM CONFIG FILE
	if(!loadSettings("config.txt"))
	{
		logger.log("Failed to load settings from configuration file", 1);
		exit(1);  
	}

}

bool Core::loadSettings(string filename)
{
	fstream configfile;

	// TODO get full directory, check file extension
	configfile.open(filename.c_str(), fstream::in);

	if(configfile.is_open())
	{
		// prepare string stream
		string buffer; buffer.empty();
		string settingType; settingType.empty();
		string settingValue; settingValue.empty();
		size_t pos;

		bool fail = false;
		int i = 0;
		while( !configfile.eof() )
		{
			i += 1;
			getline(configfile,buffer);
			pos = buffer.find(" = ");
			if( pos == string::npos )
			{
				logger.log("There is something wrong with configfile ( missing \" = \" ?) at line: ",i,1);
				fail = true;
				break;
			}
			settingType = buffer.substr(0,pos);
			settingValue = buffer.substr(pos+3);
			istringstream strm(settingValue);
			if( settingType == "width" )
			{
				if( !(strm >> settings.screen_width) )
				{
					logger.log("[configfile]: Failed to load screen width value. Line: ", 1, 1);
					fail = true;
					break;
				}
				else if( (settings.screen_width != 1600) && (settings.screen_width != 1280) && (settings.screen_width != 1024) && (settings.screen_width !=800) )
				{
					logger.log("[configfile]: This screen width is not supported: ", settings.screen_width, 1);
					fail = true;
					break;
				}
			}
			else if ( settingType == "height" )
			{
				if( !(strm >> settings.screen_height) )
				{
					logger.log("[configfile]: Failed to load screen height value. Line: ", i, 1);
					fail = true;
					break;
				}
				else if( (settings.screen_height > settings.screen_width) || (settings.screen_height != 1024) && (settings.screen_height != 900) && (settings.screen_height !=768) &&  (settings.screen_height != 600) )
				{
					logger.log("[configfile]: This screen height is not supported (in current configuration): ", settings.screen_height, 1);
					fail = true;
					break;
				}
			}
			else if ( settingType == "fullscreen" )
			{
				if( !(strm >> settings.full_screen) )
					logger.log("[configfile]: Failed to load full screen setting. Using default. Line: ", i, 1);

				else if( settings.full_screen > 1 )
				{
					logger.log("[configfile warning]: full_screen should be 0 or 1. Line ", i, 0);
					settings.full_screen = 1;
				}
				else if( settings.full_screen < 0 )
				{
					logger.log("[configfile warning]: full_screen should be 0 or 1. Line ", i, 0);
					settings.full_screen = 0;
				}
			}
			else if ( settingType == "loglevel" )
			{
				if( !(strm >> settings.log_level)  )
					logger.log("[configfile]: Failed to load log file level setting. Using default. Line: ", i, 1);
				else if( settings.log_level > 2 )
				{
					logger.log("[configfile warning]: log_level should be 0, 1 or 2. Line ", i, 0);
					settings.log_level = 2;
				}
				else if( settings.log_level < 0  )
				{
					logger.log("[configfile warning]: log_level should be 0, 1 or 2. Line ", i, 0);
					settings.log_level = 0;
				}
			}
			else if ( settingType == "cameraspeed" )
			{
				size_t found = settingValue.find(",");
				if( found != string::npos )
				{
					logger.log("[configfile]: Failed to load camera speed factor. Use \".\" instead of \",\" . Line: ", i, 1);
					fail = true;
					break;
				}
				if( !(strm >> settings.cameraSpeed) )
					logger.log("[configfile]: Failed to load camera speed factor. Using default. Line: ", i, 1);

			}
			else if ( settingType == "mousespeed" )
			{
				size_t found = settingValue.find(",");
				if( found != string::npos )
				{
					logger.log("[configfile]: Failed to load camera mouse speed factor. Use \".\" instead of \",\" . Line: ", i, 1);
					fail = true;
					break;
				}
				if( !(strm >> settings.mouseSpeed) )
					logger.log("[configfile]: Failed to load camera mouse speed factor. Using default. Line: ", i, 1);

			}
			else if ( settingType == "mediapath" )
			{
				if( !(strm >> settings.media_path) )
				{
					logger.log("[configfile]: Failed to load path for media files. Line: ", i, 1);
					fail = true;
					break;
				}
			}
			else if ( settingType == "fog" )
			{
				if( !(strm >> settings.fog) )
					logger.log("[configfile warning]: Failed to load fog setting. Using default. Line: ", i, 1);
				else if( settings.fog > 1 )
				{
					logger.log("[configfile warning]: fog should be 0, 1 . Line ", i, 0);
					settings.fog = 1;
				}
				else if( settings.fog < 0  )
				{
					logger.log("[configfile warning]: fog should be 0, 1 . Line ", i, 0);
					settings.fog = 0;
				}
			}
			else if ( settingType == "fogstart" )
			{
				if( !(strm >> settings.fogstart) )
					logger.log("[configfile warning]: Failed to load fogstart setting. Using default. Line: ", i, 1);
			}
			else if ( settingType == "fogend" )
			{
				if( !(strm >> settings.fogend) )
					logger.log("[configfile warning]: Failed to load fogend setting. Using default. Line: ", i, 1);
			}
			else if ( settingType == "fov" )
			{
				if( !(strm >> settings.fov) )
					logger.log("[configfile warning]: Failed to load fov setting. Using default. Line: ", i, 1);
				if( (settings.fov < 0.0f) || (settings.fov > 90.0f) )
				{
					settings.fov = 45.0f;
					logger.log("[configfile warning]: Bad fov setting. Using default. Line: ", i, 1);
				}
			}
			else if ( settingType == "nearcl" )
			{
				if( !(strm >> settings.nearcl) )
					logger.log("[configfile warning]: Failed to load nearcl setting. Using default. Line: ", i, 1);
			}
			else if ( settingType == "farcl" )
			{
				if( !(strm >> settings.farcl) )
					logger.log("[configfile warning]: Failed to load farcl setting. Using default. Line: ", i, 1);
			}
			else if ( settingType == "fps" )
			{
				if( !(strm >> settings.fps) )
					logger.log("[configfile warning]: Failed to load fps setting. Using default. Line: ", i, 1);
				else if( settings.fps < 0 )
				{
					settings.fps = 60;
					logger.log("[configfile warning]: FPS value should be positive. Using default. Line: ", i, 1);
				}
			}
			else if ( settingType == "text_output" )
			{
				if( !(strm >> settings.text_output) )
					logger.log("[configfile warning]: Failed to load text_output setting. Using default. Line: ", i, 1);
				else if( ( settings.text_output != 0 ) && ( settings.text_output != 1 ) )
				{
					settings.fps = 0;
					logger.log("[configfile warning]: Failed to load text_output setting. Possible values: 0 or 1. Using default. Line: ", i, 1);
				}
			}
			else if ( settingType == "video_output" )
			{
				if( !(strm >> settings.video_output) )
					logger.log("[configfile warning]: Failed to load video_output setting. Using default. Line: ", i, 1);
				else if( ( settings.video_output != 0 ) && ( settings.video_output != 1 ) )
				{
					settings.video_output = 1;
					logger.log("[configfile warning]: Failed to load video_output setting. Possible values: 0 or 1. Using default. Line: ", i, 1);
				}
			}
			else if ( settingType == "blending" )
			{
				if( !(strm >> settings.blending) )
					logger.log("[configfile warning]: Failed to load blending setting. Using default. Line: ", i, 1);
				else if( ( settings.blending != 0 ) && ( settings.blending != 1 ) )
				{
					settings.blending = 0;
					logger.log("[configfile warning]: Failed to load blending setting. Possible values: 0 or 1. Using default. Line: ", i, 1);
				}
			}
		}

		configfile.close();
		if(fail)
			return false;

	}
	else
	{
		logger.log("Failed to open config file", 1);
		return false;
	}

	// Update logger system level
	logger.setLevel(Core::getSettings().log_level);

	return true;
}

Settings Core::settings;
bool Core::SDLinit()
{
	if(settings.text_output)
	{
		std::cout << app_name << " version: " << app_ver << std::endl;
		std::cout << "Initializing..." << std::endl;
	}
	if(settings.video_output)
	{
		if(SDL_Init(SDL_INIT_VIDEO) < 0)
		{
			logger.log("Video initialization failed", 1);
			return false;
		}
		logger.log("Video initialization: OK", 0);
		if(settings.text_output)
			printf("Video initialization: OK\n");

		const SDL_VideoInfo* info = SDL_GetVideoInfo();

		if(!info)
		{
			logger.log("Video query failed", 1);
			return false;
		}
		logger.log("Video query: OK", 0);
		if(settings.text_output)
			printf("Video query: OK\n");

		if( SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5) == -1 )
		{
			logger.log("SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5) failed", 1);
			return false;
		}
		if( SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5) == -1)
		{
			logger.log("SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5) failed", 1);
			return false;
		}
		if( SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5) == -1 )
		{
			logger.log("SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5) failed", 1);
			return false;
		}
		if( SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16) == -1 )
		{
			logger.log("SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16) failed", 1);
			return false;
		}
		if( SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) == -1 )
		{
			logger.log("SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) failed", 1);
			return false;
		}

		logger.log("SDL_GL_SetAttribute: OK", 0);
		if(settings.text_output)
			printf("SDL_GL_SetAttribute: OK\n");

		int flags = 0;
		if (settings.full_screen)
			flags = SDL_OPENGL | SDL_FULLSCREEN;
		else
			flags = SDL_OPENGL;

		int screen_bits = info->vfmt->BitsPerPixel;
		logger.log("Screen bits: ", screen_bits, 0);
		if(settings.text_output)
			printf("Screen bits: %d\n", screen_bits);

		if(SDL_SetVideoMode(settings.screen_width, settings.screen_height, screen_bits, flags) == 0)
		{
			logger.log("Video mode set failed",1);
			return false;
		}
		logger.log("Video mode set: OK", 0);
		if(settings.text_output)
			printf("Video mode set: OK\n");

		SDL_WM_SetCaption("MOS3D", "MOS3D");
		SDL_ShowCursor(SDL_DISABLE);
		SDL_WarpMouse(100, 100);

		// TODO assure this
		if(TTF_Init())
			return false;

	}
	return true;
}

void Core::handleKeyDown(SDL_keysym* keysym)
{
	switch(keysym->sym)
	{
	case SDLK_ESCAPE:
		running = false;
		break;
	default:
		keys[keysym->sym]=1;
		break;
	}
	return;
}


void Core::handleKeyUp(SDL_keysym* keysym)
{
	keys[keysym->sym]=0;
	return;
}

void Core::handleSDLEvents()
{
	if(!settings.video_output)
		return;

	SDL_Event event;

	while(SDL_PollEvent(&event))
	{

		switch(event.type)
		{

		case SDL_KEYDOWN:
			handleKeyDown(&event.key.keysym);
			break;

		case SDL_KEYUP:
			handleKeyUp(&event.key.keysym);
			break;

		}

	}

	return;
}

void Core::handleConsoleEvents()
{
	char key;
	while(1)
	{
		key = _getch();
		if(key == 27)
			running = false;
	}
}

void Core::waitFrame(int fps)
{
	static int next_tick = 0;
	int this_tick;

	this_tick = SDL_GetTicks();
	if (this_tick < next_tick)
		SDL_Delay(next_tick-this_tick);
	next_tick = SDL_GetTicks() + (1000/fps);

	return;
}

